﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Linq.Expressions;

namespace SimpleData.ObjectModel
{
	public class Document : DynamicObject, IDynamicMetaObjectProvider
	{
		/// <summary>
		/// 
		/// </summary>
		/// <param name="id"></param>
		/// <param name="schema"></param>
		public Document( DocumentToken id, DocumentSchema schema )
		{
			this.Id = id;
			this.Schema = schema;
			this.Values = new DocumentValueCollection();
		}

		public DocumentToken Id
		{ get; private set; }

		public DocumentSchema Schema
		{ get; private set; }

		/// <summary>
		/// Obtém ou define os valores do documento
		/// </summary>
		public DocumentValueCollection Values
		{
			get;
			private set;
		}

		#region IDynamicMetaObjectProvider Members

		public DynamicMetaObject GetMetaObject( System.Linq.Expressions.Expression parameter )
		{
			return this.Schema.CreateMetaObject( parameter, this );
		}

		#endregion
	}


	/// <summary>
	/// Coleção de valores do documento
	/// </summary>
	public sealed class DocumentValueCollection : Dictionary<IDataObjectName, object>
	{



		public object GetValueOrDefault( IDataObjectName name )
		{
			object value;

			if ( base.TryGetValue( name, out value ) )
				return value;
			else
				return null;
		}
	}

	public class DocumentSchema
	{
		private Lazy<IList<Field>> fields;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="qualifiedName"></param>
		public DocumentSchema( DataObjectName qualifiedName )
		{
			this.QualifiedName = qualifiedName;
			this.fields = new Lazy<IList<Field>>( this.CreateListField );
		}

		public DataObjectName QualifiedName
		{ get; private set; }

		/// <summary>
		/// Obtém a coleção de campos do schema
		/// </summary>
		public IList<Field> Fields
		{
			get { return this.fields.Value; }
		}

		/// <summary>
		/// Realiza a criação da coleção de campos do schema
		/// </summary>
		/// <returns></returns>
		protected virtual IList<Field> CreateListField()
		{
			return new List<Field>();
		}

		// TODO : Ver a possibilidade do schema possuir uma versão. Talvez esse campo vai fazer parte do QualifiedName
		public int Version
		{
			get { throw new NotImplementedException(); }
		}

		public virtual DynamicMetaObject CreateMetaObject( Expression parameter, Document doc )
		{
			return new DynamicMetaObject( parameter, BindingRestrictions.Empty, doc );
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public abstract class DocumentSchemaMetaObject : DynamicMetaObject
	{

		protected DocumentSchemaMetaObject( Expression parameter, Document doc )
			: base( parameter, BindingRestrictions.Empty, doc )
		{

		}

		/// <summary>
		/// 
		/// </summary>
		public Document Document
		{
			get { return ( Document )base.Value; }
		}

		
	}

	public class Field
	{
		public Field( DataObjectName name )
		{
			this.QualifiedName = name;
		}


		public string DisplayName
		{ get; set; }

		/// <summary>
		/// Obtém ou define uma pequena ajuda para exibir para o usuário.
		/// </summary>
		/// <remarks>
		/// Esse valor será apresentado como um exemplo
		/// </remarks>
		public string ToolTip
		{ get; set; }
	
		public bool Navigable
		{ get; set; }

		/// <summary>
		/// Define se o campo é pesquisável
		/// </summary>
		public bool Searcheable
		{ get; set; }

		/// <summary>
		/// Obtém ou define o nome que identifica unicamente esse campo no sistema
		/// </summary>
		public DataObjectName QualifiedName
		{ get; private set; }

		/// <summary>
		/// Obtém ou define o tipo do campo
		/// </summary>
		public FieldType Type
		{ get; private set; }
	}


	public class FieldType
	{

		public Type FrameworkType
		{ get; private set; }
	}

	public class DataObjectName : IDataObjectName
	{
		public DataObjectName( string ns, string name )
		{
			this.Namespace = ns;
			this.Name = name;
			this.FullName = String.Concat( this.Namespace, ":", this.Name );
		}

		/// <summary>
		/// 
		/// </summary>
		public string Namespace
		{ get; private set; }

		/// <summary>
		/// 
		/// </summary>
		public string Name
		{ get; private set; }

		public bool NameEquals( string name )
		{
			return this.Name.Equals( name, StringComparison.CurrentCultureIgnoreCase );
		}

		#region IDataObjectName Members

		public string FullName
		{ get; private set; }

		#endregion

		public override bool Equals( object obj )
		{
			return this.FullName.Equals( Convert.ToString( obj ), StringComparison.InvariantCultureIgnoreCase );
		}

		public override int GetHashCode()
		{
			return this.FullName.GetHashCode();
		}

		public override string ToString()
		{
			return this.FullName;
		}
	}


	public interface IDataObjectName 
	{
		string FullName
		{ get; }
	}
	
	//public abstract class DocumentSchema : DynamicMetaObject
	//{

	//}
}
